package com.reformer.commons.utils;

import com.alibaba.fastjson.annotation.JSONField;
import com.reformer.commons.annotation.IgnoreOut;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * <li>userName -> user_name</li>
 */
public class CamelCase2UnderlineConverter {

    private CamelCase2UnderlineConverter() {
    }

    public static Map<String, Object> fromObject(Object o) {
        if (o == null || o.getClass() == Object.class)
            return new HashMap<String, Object>();
        Map<String, Object> ret = new HashMap<String, Object>();
        if (o instanceof Collection) {
            return null;
        } else if (o instanceof Map) {
            Map map = (Map) o;
            for (Object key : map.keySet()) {
                Object value = map.get(key);
                if (key != null && value != null) {
                    if (key.getClass() == String.class) {
                        key = convertJavaField2Api(key.toString());
                    }
                    ret.put(key.toString(), tranform(value));
                }
            }
        } else {
            try {
                Class<?> clazz = o.getClass();
                BeanInfo beanInfo = Introspector.getBeanInfo(clazz, Object.class);
                PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
                for (int i = 0; i < descriptors.length; i++) {
                    String key = descriptors[i].getName();
                    try {
                        Field field = clazz.getDeclaredField(key);
                        // field.setAccessible(true);
                        if (Modifier.isTransient(field.getModifiers()))
                            continue;
                        if (field.getAnnotation(IgnoreOut.class) != null)
                            continue;
                        JSONField jsonField = field.getAnnotation(JSONField.class);
                        if (jsonField != null && !jsonField.serialize())
                            continue;
                    } catch (Exception e) {
                    }
                    Object value = descriptors[i].getReadMethod().invoke(o);
                    if (key != null && value != null) {
                        ret.put(convertJavaField2Api(key), tranform(value));
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return ret;
    }

    private static Object tranform(Object value) {
        if (value instanceof Number || value instanceof Boolean || value instanceof Character) {
            return value.toString();
        }
        if (value instanceof Date || value instanceof String) {
            return value;
        }
        if (value instanceof Collection) {
            return fromCollection((Collection) value);
        }
        if (value.getClass().isArray()) {
            return fromArray(value);
        }
        return fromObject(value);
    }

    public static List<?> fromCollection(Collection col) {
        List ret = new LinkedList();
        for (Object o : col) {
            if (o == null)
                continue;
            ret.add(tranform(o));
        }
        return ret;
    }

    public static List<?> fromArray(Object array) {
        List ret = new LinkedList();
        for (int i = 0; i < Array.getLength(array); i++) {
            Object o = Array.get(array, i);
            if (o == null)
                continue;
            ret.add(tranform(o));
        }
        return ret;
    }

    public static String convertJavaField2Api(String field) {
        if (field == null) {
            return null;
        }
        field = field.trim();
        if (field.length() == 1) {
            return field.toLowerCase();
        } else {
            StringBuilder result = new StringBuilder(field.substring(0, 1).toLowerCase());
            for (char c : field.substring(1).toCharArray()) {
                if (Character.isWhitespace(c)) {
                    result.append("_");
                    continue;
                }
                if (Character.isUpperCase(c)) {
                    result.append("_").append(Character.toLowerCase(c));
                } else {
                    result.append(c);
                }
            }
            return result.toString().replaceAll("_{2,}", "_");
        }
    }

}
